/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.icanci.loopstack.damc.admin.biz.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import cn.icanci.loopstack.damc.admin.biz.mapper.RegisterTableMapper;
import cn.icanci.loopstack.damc.admin.biz.service.AppService;
import cn.icanci.loopstack.damc.admin.biz.service.ClassScriptService;
import cn.icanci.loopstack.damc.admin.biz.service.RegisterTableService;
import cn.icanci.loopstack.damc.admin.biz.service.model.ClassMethodSelector;
import cn.icanci.loopstack.damc.admin.biz.service.model.DcResponse;
import cn.icanci.loopstack.damc.admin.common.model.TextValue;
import cn.icanci.loopstack.damc.admin.common.model.base.AppVO;
import cn.icanci.loopstack.damc.admin.common.model.base.ClassScriptVO;
import cn.icanci.loopstack.damc.admin.common.model.base.RegisterTableVO;
import cn.icanci.loopstack.damc.admin.dal.common.PageList;
import cn.icanci.loopstack.damc.admin.dal.mongo.daointerface.RegisterTableDAO;
import cn.icanci.loopstack.damc.admin.dal.mongo.dateobject.RegisterTableDO;
import cn.icanci.loopstack.damc.api.http.Client;
import cn.icanci.loopstack.damc.api.http.HttpClientImpl;
import cn.icanci.loopstack.damc.spi.model.UriConstant;
import cn.icanci.loopstack.damc.spi.model.pubsub.LoadScriptDTO;
import cn.icanci.loopstack.damc.spi.model.pubsub.RegisterDTO;
import cn.icanci.loopstack.damc.spi.model.refresh.DynamicConditionRequest;
import cn.icanci.loopstack.damc.spi.model.refresh.DynamicConditionResponse;
import cn.icanci.loopstack.damc.spi.model.refresh.RefreshCell;

/**
 * @author icanci
 * @since 1.0 Created in 2025/05/16 16:59
 */
@Service("registerTableService")
public class RegisterTableServiceImpl implements RegisterTableService {
    private static final Logger logger         = LoggerFactory.getLogger(RegisterTableServiceImpl.class);
    @Resource
    private RegisterTableDAO    registerTableDAO;
    @Resource
    private RegisterTableMapper registerTableMapper;
    @Resource
    private ClassScriptService  classScriptService;
    @Resource
    private AppService          appService;
    /** http实例 */
    private static final Client CLIENT         = HttpClientImpl.getInstance();

    /** 注册请求地址 */
    private static final String REQ_URL_FORMAT = "http://%s:%s%s";

    private static final String LABEL_FORMAT   = "[%s][%s][%s]";

    /**
     * 服务注册
     *
     * @param register register
     */
    @Override
    public void register(RegisterDTO register) {
        String appCode = register.getAppCode();
        String clientAddress = register.getClientAddress();
        int clientPort = register.getClientPort();
        RegisterTableDO dbRegister = registerTableDAO.queryUnionOne(appCode, clientAddress, clientPort);
        if (dbRegister == null) {
            RegisterTableVO registerTableVO = new RegisterTableVO();
            registerTableVO.setAppCode(register.getAppCode());
            registerTableVO.setClientAddress(register.getClientAddress());
            registerTableVO.setClientPort(register.getClientPort());
            registerTableVO.setRegisterTime(new Date());
            registerTableVO.setLastUpdateTime(new Date());
            save(registerTableVO);
        } else {
            dbRegister.setLastUpdateTime(new Date());
            dbRegister.setDeleted(false);
            registerTableDAO.update(dbRegister);
        }
    }

    /**
     * 根据appCode查询所有的有效注册表
     *
     * @param appCode appCode
     * @return 注册表
     */
    @Override
    public List<RegisterTableVO> queryEnableByAppCode(String appCode) {
        return registerTableMapper.dos2vos(registerTableDAO.queryEnableByAppCode(appCode));
    }

    /**
     * 根据appCode和clazzName查询所有的函数
     *
     * @param appCode   appCode
     * @param clazzName clazzName
     * @return List<TextValue>
     */
    @Override
    public List<TextValue> loadSelector(String appCode, String clazzName) {
        List<RegisterTableVO> registerTables = queryEnableByAppCode(appCode);
        if (CollectionUtils.isEmpty(registerTables)) {
            throw new IllegalStateException("当前应用[" + appCode + "]的注册表不存在存活实例，请检查");
        }
        for (RegisterTableVO registerTable : registerTables) {
            DynamicConditionRequest request = new DynamicConditionRequest();
            request.setClasses(Lists.newArrayList(clazzName));

            List<TextValue> textValues = querySelectors(registerTable, request);
            if (textValues != null) {
                return textValues;
            }
        }
        throw new IllegalStateException("当前应用[" + appCode + "]的查询失败，请检查");
    }

    /**
     * loadScript
     *
     * @param loadScript loadScript
     * @return List<RefreshCell>
     */
    @Override
    public List<RefreshCell> loadScript(LoadScriptDTO loadScript) {
        AppVO appVO = appService.queryByCode(loadScript.getAppCode());
        if (appVO == null || appVO.isDeleted()) {
            return Lists.newArrayList();
        }

        List<ClassScriptVO> classScripts = classScriptService.queryEnableByAppCode(loadScript.getAppCode());

        Map<String, List<ClassScriptVO>> group = classScripts.stream().collect(Collectors.groupingBy(ClassScriptVO::getClazzName));

        List<RefreshCell> cells = Lists.newArrayList();
        for (Map.Entry<String, List<ClassScriptVO>> entry : group.entrySet()) {
            String clazzName = entry.getKey();
            List<RefreshCell.Cell> innerCells = Lists.newArrayList();
            for (ClassScriptVO cache : entry.getValue()) {
                RefreshCell.Cell innerCell = new RefreshCell.Cell();
                innerCell.setMethodName(cache.getMethodName());
                innerCell.setDescriptor(cache.getDescriptor());
                innerCell.setScript(cache.getScript());
                innerCells.add(innerCell);
            }

            RefreshCell cell = new RefreshCell();
            cell.setClazz(clazzName);
            cell.setCells(innerCells);
            cells.add(cell);
        }
        return cells;
    }

    /**
     * 查询所有 T
     *
     * @return 返回所有T
     */
    @Override
    public List<RegisterTableVO> queryAll() {
        return registerTableMapper.dos2vos(registerTableDAO.queryAll());
    }

    /**
     * 保存或者更新一条信息
     *
     * @param registerTableVO t
     */
    @Override
    public void save(RegisterTableVO registerTableVO) {
        if (doInsert(registerTableVO)) {
            RegisterTableDO insert = registerTableMapper.vo2do(registerTableVO);
            registerTableDAO.insert(insert);
        } else {
            registerTableDAO.update(registerTableMapper.vo2do(registerTableVO));
        }
    }

    /**
     * 根据id进行查询
     *
     * @param id id
     * @return 返回查询的结果
     */
    @Override
    public RegisterTableVO queryById(String id) {
        return registerTableMapper.do2vo(registerTableDAO.queryOneById(id));
    }

    /**
     * 分页查询
     *
     * @param registerTableVO T
     * @param pageNum         pageNum
     * @param pageSize        pageSize
     * @return 返回分页查询结果
     */
    @Override
    public PageList<RegisterTableVO> queryPage(RegisterTableVO registerTableVO, int pageNum, int pageSize) {
        PageList<RegisterTableDO> pageQuery = registerTableDAO.pageQuery(registerTableMapper.vo2do(registerTableVO), pageNum, pageSize);
        return new PageList<>(registerTableMapper.dos2vos(pageQuery.getData()), pageQuery.getPaginator());
    }

    /**
     * querySelectors
     *
     * @param registerTable registerTable
     * @param request request
     */
    private static List<TextValue> querySelectors(RegisterTableVO registerTable, DynamicConditionRequest request) {
        try {
            String reqUrl = String.format(REQ_URL_FORMAT, registerTable.getClientAddress(), registerTable.getClientPort(), UriConstant.CONDITION);

            Client.RpcRequest rpcRequest = new Client.RpcRequest(reqUrl, request, Maps.newHashMap(), Method.POST, 3, TimeUnit.SECONDS, 0);

            String message = CLIENT.call(rpcRequest, String.class);
            logger.info("[RegisterTableService][loadSelector] loadSelector result:{}", message);

            DcResponse response = JSONUtil.toBean(message, DcResponse.class);
            if (response == null || !response.isSuccess()) {
                return null;
            }
            if (CollectionUtils.isEmpty(response.getContent())) {
                return Lists.newArrayList();
            }
            List<TextValue> textValues = Lists.newArrayList();
            for (DynamicConditionResponse resp : response.getContent()) {
                String clazz = resp.getClazz();
                if (CollectionUtils.isEmpty(resp.getCells())) {
                    continue;
                }
                for (DynamicConditionResponse.Cell cell : resp.getCells()) {
                    String labelAndValue = String.format(LABEL_FORMAT, clazz, cell.getMethodName(), cell.getDescriptor());
                    textValues.add(new TextValue(labelAndValue, labelAndValue, new ClassMethodSelector(clazz, cell.getMethodName(), cell.getDescriptor())));
                }
            }
            return textValues;
        } catch (Exception e) {
            logger.error(e.getMessage());
            return null;
        }
    }

}
